package cn.juque.datapipeline.datasource;

import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.juque.common.constants.MessageEnum;
import cn.juque.common.exception.AppException;
import cn.juque.datapipeline.api.enums.DbEnum;
import cn.juque.datapipeline.bo.WriteParamBO;
import cn.juque.datapipeline.config.PropertiesConfig;
import cn.juque.datapipeline.converter.ResultSetConverter;
import cn.juque.datapipeline.entity.DatabaseInfo;
import cn.juque.datapipeline.utils.PreparedStatementUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.sql.*;
import java.util.List;
import java.util.Map;

/**
 * @author juque
 * @version 1.0.0
 * <ul>
 *     <li>MysqlDatasource</li>
 * </ul>
 * @date 2023-04-04 22:22:04
 **/
@Slf4j
@Service("oracleDatasource")
public class OracleDatasource extends AbstractDatasource {

    @Resource
    private PropertiesConfig propertiesConfig;

    @Resource
    private ResultSetConverter resultSetConverter;

    /**
     * 标志当前驱动的db类型
     *
     * @param dbEnum DbEnum
     */
    @Override
    public void markDb(DbEnum dbEnum) {
        super.markDb(DbEnum.ORACLE);
    }

    /**
     * 创建链接
     *
     * @param databaseInfo 驱动信息
     * @return 读写驱动
     */
    @Override
    public Object createConnection(DatabaseInfo databaseInfo) {
        try {
            return this.createOracleConnection(
                    databaseInfo.getDriverInfo(),
                    databaseInfo.getUserName(),
                    databaseInfo.getPassword(),
                    databaseInfo.getRemoteUrl());
        } catch (Exception e) {
            log.error("创建oracle驱动失败，驱动id:{},error:{}", databaseInfo.getId(), e.getMessage(), e);
            throw new AppException(MessageEnum.SYSTEM_ERROR.getCode(), "创建oracle驱动异常");
        }
    }

    /**
     * 读取数据
     *
     * @param connection 连接
     * @param script     脚本
     * @return 结果集
     */
    @Override
    public List<Map<String, Object>> inData(Object connection, String script) {
        if (!(connection instanceof Connection)) {
            throw new AppException(MessageEnum.SYSTEM_ERROR.getCode(), "连接方式检测非法");
        }
        Connection mysqlConnection = (Connection) connection;
        try (PreparedStatement statement = mysqlConnection.prepareStatement(script)) {
            ResultSet resultSet = statement.executeQuery();
            List<Map<String, Object>> list = this.resultSetConverter.resultSetToList(resultSet);
            resultSet.close();
            return list;
        } catch (Exception e) {
            log.error("读取数据出现异常, error:{}", e.getMessage(), e);
            throw new AppException(MessageEnum.SYSTEM_ERROR.getCode(), "读取数据出现异常");
        }
    }

    /**
     * 插入
     *
     * @param writeParamBO 参数
     * @return 操作的数据量
     */
    @Override
    public Long insert(WriteParamBO writeParamBO) {
        try {
            return PreparedStatementUtil.batchInsert(writeParamBO);
        } catch (Exception e) {
            log.error("oracle insert error: {}", e.getMessage(), e);
            throw new AppException(MessageEnum.SYSTEM_ERROR.getCode(), "oracle insert error");
        }
    }

    /**
     * 更新
     *
     * @param writeParamBO 参数
     * @return 操作的数据量
     */
    @Override
    public Long update(WriteParamBO writeParamBO) {
        try {
            return PreparedStatementUtil.batchUpdate(writeParamBO);
        } catch (Exception e) {
            log.error("oracle update error: {}", e.getMessage(), e);
            throw new AppException(MessageEnum.SYSTEM_ERROR.getCode(), "oracle update error");
        }
    }

    /**
     * 更新或删除
     *
     * @param writeParamBO 参数
     * @return 操作的数据量
     */
    @Override
    public Long saveOrUpdate(WriteParamBO writeParamBO) {
        return null;
    }

    /**
     * 删除
     *
     * @param writeParamBO 参数
     * @return 操作的数据量
     */
    @Override
    public Long delete(WriteParamBO writeParamBO) {
        try {
            return PreparedStatementUtil.batchDelete(writeParamBO);
        } catch (Exception e) {
            log.error("oracle delete error: {}", e.getMessage(), e);
            throw new AppException(MessageEnum.SYSTEM_ERROR.getCode(), "oracle delete error");
        }
    }

    /**
     * 清空目标表的数据
     *
     * @param databaseInfo 驱动信息
     * @param tableName    表名
     */
    @Override
    public void truncate(DatabaseInfo databaseInfo, String tableName) {
        String template = "TRUNCATE TABLE {}";
        String sql = CharSequenceUtil.format(template, tableName);
        Connection connection = (Connection) this.createConnection(databaseInfo);
        try (PreparedStatement statement = connection.prepareStatement(sql)) {
            statement.execute();
        } catch (Exception e) {
            log.error("databaseId:{},truncate oracle table:{} error", databaseInfo.getId(), tableName);
            throw new AppException(MessageEnum.SYSTEM_ERROR.getCode(), "执行清空表数据异常");
        }
    }

    /**
     * 创建连接方式
     *
     * @param driver      驱动
     * @param rawUsername 加密的用户名
     * @param rawPassword 加密的密码
     * @param url         链接
     * @return Connection
     * @throws ClassNotFoundException ClassNotFoundException
     * @throws SQLException           SQLException
     */
    private Connection createOracleConnection(String driver, String rawUsername, String rawPassword, String url) throws ClassNotFoundException, SQLException {
        Class.forName(driver);
        String publicKey = this.propertiesConfig.getRsaPublicKey();
        String privateKey = this.propertiesConfig.getRsaPrivateKey();
        RSA rsa = SecureUtil.rsa(privateKey, publicKey);
        // 解密用户名、密码
        String password = rsa.decryptStr(rawPassword, KeyType.PrivateKey);
        return DriverManager.getConnection(url, rawUsername, password);
    }
}
